// 版权所有2009 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package ir

import (
	"cmd/compile/internal/base"
	"cmd/compile/internal/types"
	"cmd/internal/src"
	"fmt"
)

// 在类型检查之前表示类型语法的节点。
// 在类型检查之后，它们仅作为围绕a*类型的外壳。类型
// 调用TypeNode将转换a*类型。键入一个节点外壳。

// Ntype是一个语法上看起来像类型的节点。
// 它可以是类型检查前的原始语法，也可以是type（）设置为a*类型的OTYPE。类型注意语法并不能保证它是一个类型：表达式
// 比如*fmt是一个Ntype（我们还不知道名称是否是类型），
// 但至少1+1不是一个Ntype。
type Ntype interface {
	Node
	CanBeNtype()
}

// 迷你类型是一种迷你类型语法节点实现，
// 将作为第一个字段嵌入到更大的节点实现中。
type miniType struct {
	miniNode
	typ *types.Type
}

func (*miniType) CanBeNtype() {}

func (n *miniType) Type() *types.Type { return n.typ }

// setOTYPE将n更改为返回t的OTYPE节点。
// 以这种方式重写节点不应该是严格意义上的
// 必要的（我们应该能够用
// 正确的OTYPE节点更新用法），但现在继续这样做基本上是无害且容易的。
// 
// setOTYPE还记录t.Nod=self（如果尚未设置t.Nod）。
// （有些类型由多个OTYPE节点共享，因此只有
// /第一个这样的节点用作t.Nod。）
func (n *miniType) setOTYPE(t *types.Type, self Ntype) {
	if n.typ != nil {
		panic(n.op.String() + " SetType: type already set")
	}
	n.op = OTYPE
	n.typ = t
	t.SetNod(self)
}

func (n *miniType) Sym() *types.Sym { return nil }   // 对于格式类型
func (n *miniType) Implicit() bool  { return false } // 对于格式类型

// ChanType表示带有方向Dir的chan Elem语法。
type ChanType struct {
	miniType
	Elem Ntype
	Dir  types.ChanDir
}

func NewChanType(pos src.XPos, elem Ntype, dir types.ChanDir) *ChanType {
	n := &ChanType{Elem: elem, Dir: dir}
	n.op = OTCHAN
	n.pos = pos
	return n
}

func (n *ChanType) SetOTYPE(t *types.Type) {
	n.setOTYPE(t, n)
	n.Elem = nil
}

// MapType表示map[Key]值类型语法。
type MapType struct {
	miniType
	Key  Ntype
	Elem Ntype
}

func NewMapType(pos src.XPos, key, elem Ntype) *MapType {
	n := &MapType{Key: key, Elem: elem}
	n.op = OTMAP
	n.pos = pos
	return n
}

func (n *MapType) SetOTYPE(t *types.Type) {
	n.setOTYPE(t, n)
	n.Key = nil
	n.Elem = nil
}

// StructType表示结构{…}键入语法。
type StructType struct {
	miniType
	Fields []*Field
}

func NewStructType(pos src.XPos, fields []*Field) *StructType {
	n := &StructType{Fields: fields}
	n.op = OTSTRUCT
	n.pos = pos
	return n
}

func (n *StructType) SetOTYPE(t *types.Type) {
	n.setOTYPE(t, n)
	n.Fields = nil
}

// 接口类型表示结构{…}键入语法。
type InterfaceType struct {
	miniType
	Methods []*Field
}

func NewInterfaceType(pos src.XPos, methods []*Field) *InterfaceType {
	n := &InterfaceType{Methods: methods}
	n.op = OTINTER
	n.pos = pos
	return n
}

func (n *InterfaceType) SetOTYPE(t *types.Type) {
	n.setOTYPE(t, n)
	n.Methods = nil
}

// FuncType表示func（Args）结果类型语法。
type FuncType struct {
	miniType
	Recv    *Field
	Params  []*Field
	Results []*Field
}

func NewFuncType(pos src.XPos, rcvr *Field, args, results []*Field) *FuncType {
	n := &FuncType{Recv: rcvr, Params: args, Results: results}
	n.op = OTFUNC
	n.pos = pos
	return n
}

func (n *FuncType) SetOTYPE(t *types.Type) {
	n.setOTYPE(t, n)
	n.Recv = nil
	n.Params = nil
	n.Results = nil
}

// 字段是声明的结构字段、接口方法或函数参数。
// 它不是一个节点。
type Field struct {
	Pos      src.XPos
	Sym      *types.Sym
	Ntype    Ntype
	Type     *types.Type
	Embedded bool
	IsDDD    bool
	Note     string
	Decl     *Name
}

func NewField(pos src.XPos, sym *types.Sym, ntyp Ntype, typ *types.Type) *Field {
	return &Field{Pos: pos, Sym: sym, Ntype: ntyp, Type: typ}
}

func (f *Field) String() string {
	var typ string
	if f.Type != nil {
		typ = fmt.Sprint(f.Type)
	} else {
		typ = fmt.Sprint(f.Ntype)
	}
	if f.Sym != nil {
		return fmt.Sprintf("%v %v", f.Sym, typ)
	}
	return typ
}

// TODO（mdempsky）：将字段重新设置为节点，以便生成这些字段？
// 字段是go/ast和cmd/compile/internal/syntax中的节点。

func copyField(f *Field) *Field {
	if f == nil {
		return nil
	}
	c := *f
	return &c
}
func doField(f *Field, do func(Node) bool) bool {
	if f == nil {
		return false
	}
	if f.Decl != nil && do(f.Decl) {
		return true
	}
	if f.Ntype != nil && do(f.Ntype) {
		return true
	}
	return false
}
func editField(f *Field, edit func(Node) Node) {
	if f == nil {
		return
	}
	if f.Decl != nil {
		f.Decl = edit(f.Decl).(*Name)
	}
	if f.Ntype != nil {
		f.Ntype = edit(f.Ntype).(Ntype)
	}
}

func copyFields(list []*Field) []*Field {
	out := make([]*Field, len(list))
	for i, f := range list {
		out[i] = copyField(f)
	}
	return out
}
func doFields(list []*Field, do func(Node) bool) bool {
	for _, x := range list {
		if doField(x, do) {
			return true
		}
	}
	return false
}
func editFields(list []*Field, edit func(Node) Node) {
	for _, f := range list {
		editField(f, edit)
	}
}

// 切片类型表示[]元素类型语法。
// 如果DDD为真，则为。。。函数列表末尾的元素。
type SliceType struct {
	miniType
	Elem Ntype
	DDD  bool
}

func NewSliceType(pos src.XPos, elem Ntype) *SliceType {
	n := &SliceType{Elem: elem}
	n.op = OTSLICE
	n.pos = pos
	return n
}

func (n *SliceType) SetOTYPE(t *types.Type) {
	n.setOTYPE(t, n)
	n.Elem = nil
}

// 数组类型表示[Len]元素类型语法。
// 如果Len为nil，则类型为a[…]数组文字中的元素。
type ArrayType struct {
	miniType
	Len  Node
	Elem Ntype
}

func NewArrayType(pos src.XPos, len Node, elem Ntype) *ArrayType {
	n := &ArrayType{Len: len, Elem: elem}
	n.op = OTARRAY
	n.pos = pos
	return n
}

func (n *ArrayType) SetOTYPE(t *types.Type) {
	n.setOTYPE(t, n)
	n.Len = nil
	n.Elem = nil
}

// typeNode是类型t的节点包装器。
type typeNode struct {
	miniNode
	typ *types.Type
}

func newTypeNode(pos src.XPos, typ *types.Type) *typeNode {
	n := &typeNode{typ: typ}
	n.pos = pos
	n.op = OTYPE
	return n
}

func (n *typeNode) Type() *types.Type { return n.typ }
func (n *typeNode) Sym() *types.Sym   { return n.typ.Sym() }
func (n *typeNode) CanBeNtype()       {}

// typeNode返回表示类型t的节点。
func TypeNode(t *types.Type) Ntype {
	return TypeNodeAt(src.NoXPos, t)
}

// TypeNodeAt类似于typeNode，但允许指定位置
// 。
// 
// 已弃用：改用TypeNode。通常情况下，
// 匿名OTYPE节点的位置应该无关紧要。然而，TypeNodeAt是
// 可与toolstash-cmp一起使用，以重构对OTYPE位置敏感的现有代码
// 。
func TypeNodeAt(pos src.XPos, t *types.Type) Ntype {
	if n := t.Obj(); n != nil {
		if n.Type() != t {
			base.Fatalf("type skew: %v has type %v, but expected %v", n, n.Type(), t)
		}
		return n.(Ntype)
	}
	return newTypeNode(pos, t)
}

// DynamicType表示类型开关中的目标类型。
type DynamicType struct {
	miniExpr
	X    Node // a*运行时_目标类型
	ITab Node // 对于从非空接口到非接口的类型切换，这是该对的itab。
}

func NewDynamicType(pos src.XPos, x Node) *DynamicType {
	n := &DynamicType{X: x}
	n.pos = pos
	n.op = ODYNAMICTYPE
	return n
}
